﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ZeroBugBounce.Missive.Locks;
using System.Reflection;

namespace ZeroBugBounce.Missive
{
	/// <summary>
	/// The central class of Missive. Each instance is an encapsulated set of topic and type routed message handlers, 
	/// error handling, logging events and task dispatch
	/// </summary>
	public partial class Messenger
	{
		protected readonly SpinningHybridLock HandlerLock = new SpinningHybridLock(1200);
		protected IList<Handler> Handlers = new List<Handler>();
		//protected IList<HandlerType> HandlerTypes = new List<HandlerType>();

		// keeps track of all the message types we've already
		// activated, so we don't activate them again
		protected HashSet<RuntimeTypeHandle> ActivatedMessageTypeOfHandlerTypesGuard = new HashSet<RuntimeTypeHandle>();

		protected Dictionary<Tuple<Topic, Type>, Handler[]> CachedHandlers = new Dictionary<Tuple<Topic, Type>, Handler[]>();

		//protected Handler<T>[] QueryHandlers<T>(Topic topic)
		//{
		//    //// first add any handler type handlers:
		//    //var messageType = typeof(T);
		//    //if (messageType.IsGenericType && HandlerTypes.Count > 0
		//    //    && !ActivatedMessageTypeOfHandlerTypesGuard.Contains(messageType.TypeHandle))
		//    //{
		//    //    // find any handler types that could work
		//    //    var messageTypeDefinition = messageType.GetGenericTypeDefinition();
		//    //    var handlerTypes = HandlerTypes.Where(ht => ht.OpenMessageType == messageTypeDefinition.GetGenericTypeDefinition()).ToArray();

		//    //    Type[] messageTypeParameters = messageType.GetGenericArguments().ToArray();

		//    //    for (int hTypeIndex = 0; hTypeIndex < handlerTypes.Length; hTypeIndex++)
		//    //    {
		//    //        HandlerType handlerType = handlerTypes[hTypeIndex];
		//    //        var closedHandlerType = handlerType.OpenHandlerType.MakeGenericType(messageTypeParameters);
		//    //        this.AddHandler(handlerType.Topic, (Handler)Activator.CreateInstance(closedHandlerType));
		//    //    }

		//    //    ActivatedMessageTypeOfHandlerTypesGuard.Add(messageType.TypeHandle);
		//    //}

		//    var key = new Tuple<Topic, Type>(topic, typeof(T));
		//    HandlerLock.Enter();

		//    Handler<T>[] handlers = CachedHandlers[key] as Handler<T>[];
		//    //if (handlers == null)
		//    //{
		//    //    handlers = CachedHandlers[key].OfType<Handler<T>>().ToArray();
		//    //    HandlerLock.Leave();
		//    //    return handlers;
		//    //}

		//    HandlerLock.Leave();
		//    return handlers;
		//}

		public IDispatcher Dispatcher { get; set; }

		public Messenger()
		{
			Dispatcher = new TaskParallelDispatcher(this);
			DefaultTopic = Topic.Default;
			AllTopics = Topic.All;

			TopicCache[DefaultTopic.hash] = DefaultTopic;
			TopicCache[AllTopics.hash] = AllTopics;

		}

		public IEnumerable<Handler> ScanAndLoadHandlers(Assembly assembly)
		{
			HandlerLock.Enter();
			var handlerTypes = assembly.GetTypes().Where(t => typeof(Handler).IsAssignableFrom(t) && t.GetConstructor(new Type[0]) != null);

			List<Handler> handlers = new List<Handler>();
			foreach (Type handlerType in handlerTypes)
			{
				var handler = Activator.CreateInstance(handlerType) as Handler;
				handlers.Add(handler);
			}

			AddHandlers(handlers.ToArray());
			HandlerLock.Leave();
			return handlers;
		}

		public IList<Handler> AllHandlers()
		{
			HandlerLock.Enter();
			var handlers = Handlers.ToList();
			HandlerLock.Leave();

			return handlers;
		}
	}

	public class Disposable : IDisposable
	{
		Action _disposeAction;
		public Disposable(Action disposeAction)
		{
			_disposeAction = disposeAction;
		}

		public void Dispose()
		{
			if (_disposeAction != null)
			{
				_disposeAction();
			}
		}
	}

	public class EmptyDisposable : IDisposable
	{
		public void Dispose()
		{

		}
	}

	//public class HandlerType
	//{
	//    public Type OpenHandlerType { get; set; }
	//    public Type OpenMessageType { get; set; }
	//    public Topic Topic { get; set; }
	//}
}
